package its_models.expert_model.expert_help_util;

import java.util.ArrayList ;

public class PossiblePairs {

    public PossiblePairs(){
        positionsList = new ArrayList<SudokuCoordinate>();
        listOfPairedValues = new ArrayList<Integer>();
    }

    public PossiblePairs( PossiblePairs anotherObj ) {
        this.positionsList = new ArrayList<SudokuCoordinate>( anotherObj.getPossPosition() ) ;
        this.listOfPairedValues = new ArrayList<Integer>( anotherObj.getPairedValues() ) ;
    }

    /* Method to set the possible positions of those pair.  */
    /*  Precondition: the number of possible position should able to form a Pair, Triple or Quad. */
    /* (i.e. size >=2 and size <= 4) */
    public boolean setPositionList( ArrayList<SudokuCoordinate> listOfCoordinates ){
        boolean isPositionListSet = isAbleToFormFairs( listOfCoordinates ) ;
        if( isPositionListSet ){
            positionsList = new ArrayList<SudokuCoordinate>( listOfCoordinates ) ;
        }
        return isPositionListSet ;
    }

    /* Method to add an Value Index to the List of cell values to form a pair. */
    public void addValueIndex( int valueIndex ) {
        listOfPairedValues.add( valueIndex ) ;
    }


    /* Method to get the list of possible positions. */
    public ArrayList<SudokuCoordinate> getPossPosition(){
        return positionsList ;
    }

    /* Method to get the number of possible position.*/
    public int getNumOfPossPositions(){
        return positionsList.size() ;
    }


    /* Method to get the possible position at the given index*/
    public SudokuCoordinate getPossPositionAt( int index ){
        SudokuCoordinate possPositionAt = null ;

        if( (index >= 0) && (index < positionsList.size()))
        {
            possPositionAt = new SudokuCoordinate();
            possPositionAt.copy(positionsList.get(index) ) ;
        }

        return possPositionAt ;
    }

    /**
     * Method to get the value indices that formed this pair.
     *
     *  @return ArrayList<SudokuCoordinate> the list of possible positions
     */
    public ArrayList<Integer> getPairedValues()
    {
        return listOfPairedValues ;
    }

    /**
     * Method to get the number of value indices that formed this pair.
     *
     *  @return int the number of value indices
     */
    public int getNumOfPairedValues()
    {
        return listOfPairedValues.size() ;
    }

    /**
     * Method to get the value index that formed this pair at the given index.
     *
     *  @return int value index at that index
     *          if no value index is found at that index, -1 is returned
     */
    public int getValueIndexAt( int index )
    {
        int valueIndexAt = -1 ;

        if( (index >= 0) && (index < listOfPairedValues.size()))
        {
            valueIndexAt = listOfPairedValues.get( index ) ;
        }

        return valueIndexAt ;
    }

    /**
     * Method to get the number of possible pairs can be formed.
     *
     *  @param int the number of possible pairs can be formed
     *
    public int getNumOfPossPairs()
    {
        return listOfPairedValues.size() ;
    }
    */

    /**
     * Method to get the String that reprsent this Class.
     *
     *  @param String the string represeing this class.
     */
     public String toString()
     {
         String strOfPossiblePairs = "\n" ;

         strOfPossiblePairs = strOfPossiblePairs + " - Coordinates: " + positionsList.toString() + " - " + positionsList.size() + " \n"  ;
         strOfPossiblePairs = strOfPossiblePairs + " - Value Indices: " + listOfPairedValues.toString() + " - " + listOfPairedValues.size() + " \n"  ;

         return strOfPossiblePairs ;
     }


    // ~~ ~~ ~~ ~~ some supporitve function ~~ ~~ ~~ ~~ //
    /**
     * Method to check if the position list is empty
     *
     *  @return boolean true, if the postion list is empty ;
     *                  false, for any other cases.
     */
    private boolean isPositionListEmpty()
    {
        boolean isPosListEmpty = false ;

        if( (null == positionsList) || (positionsList.isEmpty()) )
        {
            isPosListEmpty = true ;
        }

        return isPosListEmpty ;
    }

    /* Method to check if a Coordinate List is possible to form pairs. */
    public static boolean isAbleToFormFairs( ArrayList<SudokuCoordinate> listOfCoordinates )
    {
        boolean isPositionListSet = false ;

        final int INT_NO_OF_COORDINATES = listOfCoordinates.size() ;

        if( (INT_NO_OF_COORDINATES >= INT_SIZE_OF_PAIRS) && ( INT_NO_OF_COORDINATES <= INT_SIZE_OF_QUAD) ){
            isPositionListSet = true ;
        }

        return isPositionListSet ;
    }

    /**
     * Method to check is a Coordinate List is possible to pair up
     * with this possible pairs.
     *  condition 1: the Coordinate List is possible to form pairs
     *  condition 2: the Coordiante List is shorter or equal size with this coordinate in this
     *
     *  @param ArrayList<SudokuCoordinate> the List of Coordinate to check with
     *  @return boolean true, if the coordinate list is able to pair up with this ;
     *                  false, for any other cases
     */
    public boolean isAbleToPairUp( ArrayList<SudokuCoordinate> listOfCoordinates )
    {
        boolean isAbleToFormPair = ( isAbleToFormFairs(listOfCoordinates) ) ;
        boolean isShortThanThis = ( listOfCoordinates.size() <= positionsList.size() ) ;

        return ( isAbleToFormPair && isShortThanThis ) ;
    }

    /**
     * Method to check if pairs (pair, triple or quad) is able to form.
     * (i.e. no. of possible position == no. of paired values)
     *
     *  @param boolean ture, if pairs are able to form
     *                 false, for any other cases
     */
    public boolean isPairsFound()
    {
        return ( !(positionsList.isEmpty() || listOfPairedValues.isEmpty()) &&
                  (positionsList.size() == listOfPairedValues.size()) ) ;
    }

    /**
     * Method to check if two ArrayList of locations for two possible value
     * are possible to form a pairs.
     * (To form a pairs a smaller ArrayList should be a SubSet of the bigger one)
     *
     *  @param ArrayList<SudokuCoordinate> the first ArrayList contains location for a possible value
     *  @param ArrayList<SudokuCoordinate> the second ArrayList contains location for another possible value
     *  @return boolean true, if they are possible to form a pair
     *                  false, for any other cases.
     */
    public boolean isPairedUpWith( ArrayList<SudokuCoordinate> positionListToCompare )
    {
        boolean isParisCanForm = true ;  // <<<------ This is Not Used during Testing !!! !!!

        // 1. check both arrayList are valid
        if( !(isPositionListEmpty()) && !(null == positionListToCompare) )
        {
            ArrayList<SudokuCoordinate> longerList ;
            ArrayList<SudokuCoordinate> shorterList ;

            // 2. figure out which list is longer
            if( positionsList.size() > positionListToCompare.size() )
            {
                longerList = new ArrayList<SudokuCoordinate>( positionsList ) ;
                shorterList = new ArrayList<SudokuCoordinate>( positionListToCompare ) ;
            }
            else
            {
                longerList = new ArrayList<SudokuCoordinate>( positionListToCompare ) ;
                shorterList = new ArrayList<SudokuCoordinate>( positionsList ) ;
            }

            // 3. try to pair up the coordinates
            final int INT_SIZE_OF_LIST = longerList.size() ;
            final int INT_SIZE_OF_LIST_TO_COMPARE = shorterList.size() ;
            for( int positionCounter = 0 ; positionCounter < INT_SIZE_OF_LIST_TO_COMPARE; ++positionCounter )
            {
                // check for pairs
                int pairsPositionIndex = indexOfSudokuCoordinate( longerList, shorterList.get(positionCounter) ) ;
                isParisCanForm = isParisCanForm && ( -1 != pairsPositionIndex ) ;
            }
        }

        return isParisCanForm ;
    }

    /**
     * Method to get the index position of a SudokuCoordinate in a ArrayList of SudokuCoordinate
     *
     *  @param ArrayList<SudokuCoordinate> the ArrayList of SudokuCoordinate
     *  @param SudokuCoordinate the SudokuCoordinate to find with
     *
     *  @return int the index of the SudokuCoordinate found;
     *              -1 if the SudokuCoordinate was not found
     */
    public static int indexOfSudokuCoordinate( ArrayList<SudokuCoordinate> positionsList, SudokuCoordinate positionToFound )
    {
        int resultIndex = -1 ;

        for( int posCounter = 0 ; posCounter < positionsList.size() ; ++posCounter )
        {
            if( positionsList.get(posCounter).equal(positionToFound) )
            {
                resultIndex = posCounter ;
            }
        }

        return resultIndex ;
    }


    /**
     * Method to check if the given Possible Paris is Equal to this.
     *
     *  @param PossibleParis the possilbe pair to check with.
     *  @boolena true, if they are equal
     *           false, for any other cases.
     */
    public boolean isEqual( PossiblePairs pairsToCheck )
    {
        boolean areTheyEqual = true ;

        final int INT_THIS_POSITIONS = positionsList.size() ;
        final int INT_THIS_VALUES = listOfPairedValues.size() ;
        final int INT_TO_CHK_POSITIONS = pairsToCheck.getNumOfPossPositions() ;
        final int INT_TO_CHK_VALUES = pairsToCheck.getNumOfPairedValues() ;

        if( (INT_THIS_POSITIONS == INT_TO_CHK_POSITIONS) &&
            (INT_THIS_VALUES == INT_TO_CHK_VALUES) )
        {
            // 1. check positions
            for( int posCounter = 0 ; (areTheyEqual && (posCounter < INT_THIS_POSITIONS) ) ; ++posCounter )
            {
                areTheyEqual = areTheyEqual &&
                               ( -1 != indexOfSudokuCoordinate(this.positionsList, pairsToCheck.getPossPositionAt( posCounter )) ) ;
                               //( -1 != positionsList.indexOf(pairsToCheck.getPossPositionAt( posCounter )) ) ;
                            //   ( this.getPossPositionAt(posCounter).equal(pairsToCheck.getPossPositionAt( posCounter )) ) ;
            }

            // 2. check values
            for( int valueCounter = 0 ; (areTheyEqual && (valueCounter < INT_THIS_VALUES) ) ; ++valueCounter )
            {
                areTheyEqual = areTheyEqual &&
                               ( -1 != listOfPairedValues.indexOf(pairsToCheck.getValueIndexAt( valueCounter )) ) ;
                               //( this.getValueIndexAt(valueCounter) == pairsToCheck.getValueIndexAt(valueCounter) ) ;
            }
        }
        else
        {
            areTheyEqual = false ;
        }

        return areTheyEqual ;
    }

    /**
     * Method to check if this is a SubSet Set of the given pair.
     *  (it means this Contains given pair)
     *
     *  @param PossibleParis the possilbe pair to check with.
     *  @boolena true, if this is a subset
     *           false, for any other cases.
     */
    public boolean isSubSet( PossiblePairs pairsToCheck )
    {
        boolean areTheyEqual = true ;

        final int INT_THIS_POSITIONS = positionsList.size() ;
        final int INT_THIS_VALUES = listOfPairedValues.size() ;
        final int INT_TO_CHK_POSITIONS = pairsToCheck.getNumOfPossPositions() ;
        final int INT_TO_CHK_VALUES = pairsToCheck.getNumOfPairedValues() ;


        if( (INT_THIS_POSITIONS < INT_TO_CHK_POSITIONS) &&
            (INT_THIS_VALUES < INT_TO_CHK_VALUES) )
        {
            // 1. check positions
            for( int posCounter = 0 ; (areTheyEqual && (posCounter < INT_THIS_POSITIONS) ) ; ++posCounter )
            {
                areTheyEqual = areTheyEqual &&
                               ( -1 != indexOfSudokuCoordinate(pairsToCheck.getPossPosition(), this.positionsList.get( posCounter )) ) ;
                               //( -1 != indexOfSudokuCoordinate(this.positionsList, pairsToCheck.getPossPositionAt( posCounter )) ) ;
                               //( -1 != positionsList.indexOf(pairsToCheck.getPossPositionAt( posCounter )) ) ;
                            //   ( this.getPossPositionAt(posCounter).equal(pairsToCheck.getPossPositionAt( posCounter )) ) ;
            }

            // 2. check values
            for( int valueCounter = 0 ; (areTheyEqual && (valueCounter < INT_THIS_VALUES) ) ; ++valueCounter )
            {
                areTheyEqual = areTheyEqual &&
                               ( -1 != pairsToCheck.getPairedValues().indexOf(this.listOfPairedValues.get( valueCounter )) ) ;
                               //( this.getValueIndexAt(valueCounter) == pairsToCheck.getValueIndexAt(valueCounter) ) ;
            }
        }
        else
        {
            areTheyEqual = false ;
        }

        return areTheyEqual ;
    }

    /**
     * Method to check if this is a SuperSet Set of the given pair.
     *  (it means this Contains given pair)
     *
     *  @param PossibleParis the possilbe pair to check with.
     *  @boolena true, if this is a super
     *           false, for any other cases.
     */
    public boolean isSuperSet( PossiblePairs pairsToCheck )
    {
        boolean areTheyEqual = true ;

        final int INT_THIS_POSITIONS = positionsList.size() ;
        final int INT_THIS_VALUES = listOfPairedValues.size() ;
        final int INT_TO_CHK_POSITIONS = pairsToCheck.getNumOfPossPositions() ;
        final int INT_TO_CHK_VALUES = pairsToCheck.getNumOfPairedValues() ;


        if( (INT_THIS_POSITIONS > INT_TO_CHK_POSITIONS) &&
            (INT_THIS_VALUES > INT_TO_CHK_VALUES) )
        {
            // 1. check positions
            for( int posCounter = 0 ; (areTheyEqual && (posCounter < INT_TO_CHK_POSITIONS) ) ; ++posCounter )
            {
                areTheyEqual = areTheyEqual &&
                               ( -1 != indexOfSudokuCoordinate(this.positionsList, pairsToCheck.getPossPositionAt( posCounter )) ) ;
                               //( -1 != positionsList.indexOf(pairsToCheck.getPossPositionAt( posCounter )) ) ;
                            //   ( this.getPossPositionAt(posCounter).equal(pairsToCheck.getPossPositionAt( posCounter )) ) ;
            }

            // 2. check values
            for( int valueCounter = 0 ; (areTheyEqual && (valueCounter < INT_TO_CHK_VALUES) ) ; ++valueCounter )
            {
                areTheyEqual = areTheyEqual &&
                               ( -1 != listOfPairedValues.indexOf(pairsToCheck.getValueIndexAt( valueCounter )) ) ;
                               //( this.getValueIndexAt(valueCounter) == pairsToCheck.getValueIndexAt(valueCounter) ) ;
            }
        }
        else
        {
            areTheyEqual = false ;
        }

        return areTheyEqual ;
    }


    // ~~ ~~ ~~ ~~ attribute for this class  ~~ ~~ ~~ ~~ //
    /* the possible position of those paired values */
    private ArrayList<SudokuCoordinate> positionsList ;

    /* the values that formed this pair */
    private ArrayList<Integer> listOfPairedValues ;

    // ~~ ~~ ~~ ~~ some fixed information  ~~ ~~ ~~ ~~ //
    /* the size of a Quad */
    private final static int INT_SIZE_OF_QUAD = 4 ;
    /* the size of a Pair */
    private final static int INT_SIZE_OF_PAIRS = 2 ;

}
